﻿// HSS.Interlink.Web.InterlinkUtilities.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.html)
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2012
// ----------------------------------------------------------------------------
// File:       InterlinkUtilities.cs
// Author:     HSS\gbanta
// Created:    07/16/2011
// Modified:   12/17/2011
// ----------------------------------------------------------------------------
namespace HSS.Interlink.Web
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using System.Security.Permissions;
	using System.Web.Hosting;
	using Microsoft.Win32;
	#endregion

	#region InterlinkUtilities
	/// <summary>
	/// Misc. file utility methods.
	/// </summary>
	public static class InterlinkUtilities
	{
		/// <summary>
		/// Gets the file Content-Type Description based on the file name and extension from the Windows Registry.
		/// </summary>
		/// <param name="fileName">The name of the file to get the Description for. (Example: file.ext)</param>
		/// <param name="defaultDescription">The default description to return if an error is encountered or not found.</param>
		/// <returns>The file description if found; otherwise the default description.</returns>
		/// <exception cref="System.Security.SecurityException">A caller higher in the call stack 
		/// does not have the permission specified by the current instance.-or- A caller higher in the call stack has called 
		/// System.Security.CodeAccessPermission.Deny() on the current permission object.</exception>
		public static string GetContentTypeDescription(string fileName, string defaultDescription = "User file")
		{
			RegistryKey typeKey = null;
			try
			{
				string ext = Path.GetExtension(fileName);
				if (string.IsNullOrEmpty(ext))
					return defaultDescription;
				ext = ext.ToLower();

				try
				{
					RegistryPermission regPerm = new RegistryPermission(RegistryPermissionAccess.Read, @"\\HKEY_CLASSES_ROOT");
					regPerm.Demand();
				}
				catch
				{
					return defaultDescription;
				}

				typeKey = Registry.ClassesRoot.OpenSubKey(ext);
				if (null != typeKey)
				{
					string fileType = typeKey.GetValue(string.Empty).ToString();
					typeKey.Close();
					typeKey = Registry.ClassesRoot.OpenSubKey(fileType);
				}

				if (null != typeKey)
				{
					defaultDescription = typeKey.GetValue(string.Empty).ToString();
				}
			}
			catch { }
			finally
			{
				if (null != typeKey)
					typeKey.Close();
			}

			return defaultDescription;
		}
		/// <summary>
		/// Gets a folder path relative to the <see cref="HostingEnvironment.ApplicationPhysicalPath"/>,
		/// creating it if doesn't already exist.
		/// </summary>
		/// <param name="folderName">The name of the folder to get/create</param>
		/// <returns>The folder's absolute path.</returns>
		/// <exception cref="ArgumentNullException">The provided folderName is null or an empty string.</exception>
		public static string GetFolder(string folderName)
		{
			if (string.IsNullOrEmpty(folderName))
				throw new ArgumentNullException("folderName");
			var folder = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, folderName);
			if (!Directory.Exists(folder))
				Directory.CreateDirectory(folder);
			return folder;
		}
		/// <summary>
		/// Purges files from the given folder, where the difference (in Total Minutes)
		/// between a file's LastWriteTime compared to DateTime.Now for each file found,
		/// is greater than the provided threshold (minutes).
		/// </summary>
		/// <param name="folder">The absolute path of the Folder to purge.</param>
		/// <param name="purgeInterval">The interval, since a file inside the folder has been written to in order to qualify for deletion.</param>
		/// <remarks>
		/// <para>
		/// This purge routine only purges the provided folder's files, it does not recurse or search sub folders.
		/// </para>
		/// </remarks>
		public static void PurgeFiles(string folder, TimeSpan purgeInterval)
		{
			if (purgeInterval.Ticks == 0)
				return;
			var threshold = DateTime.Now.Subtract(purgeInterval);
			var di = new DirectoryInfo(folder);
			if (!di.Exists)
				return;

			// Delete old files
			var files = from file in di.EnumerateFiles("*.*", SearchOption.TopDirectoryOnly)
						select file;
			foreach (var file in files)
			{
				try
				{
					if (file.LastWriteTime < threshold)
						file.Delete();
				}
				catch { }
			}
		}
		/// <summary>
		/// Purges expired files and deletes empty sub-folders.
		/// </summary>
		/// <param name="rootFolder">The root folder to evaluate.</param>
		/// <param name="purgeInterval">The interval since a given file was last written to within any folders.</param>
		/// <param name="deleteRootIfEmpty">Optionally deletes the root folder if it's empty after the purge.</param>
		public static void PurgeAndDelete(string rootFolder, TimeSpan purgeInterval, bool deleteRootIfEmpty)
		{
			if (null == purgeInterval || purgeInterval.Ticks == 0)
				return;
			var threshold = DateTime.Now.Subtract(purgeInterval);

			var di = new DirectoryInfo(rootFolder);
			if (!di.Exists)
				return;

			// Delete old files
			var files = from file in di.EnumerateFiles("*.*", SearchOption.AllDirectories)
						select file;
			foreach (var file in files)
			{
				try
				{
					if (file.LastWriteTime < threshold)
						file.Delete();
				}
				catch { }
			}


			// Delete empty folders
			di.Refresh();
			var folders = from folder in di.EnumerateDirectories("*.*", SearchOption.AllDirectories)
						  select folder;
			var emptyFolders = new List<DirectoryInfo>();
			var removed = new List<DirectoryInfo>();
			foreach (var folder in folders)
			{
				try
				{
					if (folder.EnumerateFiles().Count() == 0)
						emptyFolders.Add(folder);
				}
				catch { }
			}
			var count = emptyFolders.Count;
			while (count > 0)
			{
				foreach (var dir in emptyFolders)
				{
					try
					{
						dir.Refresh();
						if (dir.Exists && dir.EnumerateFileSystemInfos().Count() == 0)
						{
							dir.Delete();
							removed.Add(dir);
							count--;
						}
					}
					catch { }
				}
				try
				{
					foreach (var rdi in removed)
						emptyFolders.Remove(rdi);
					removed.Clear();
				}
				catch { }
			}

			// Delete root folder
			if (deleteRootIfEmpty)
			{
				di.Refresh();
				try
				{
					if (di.EnumerateFileSystemInfos().Count() == 0)
						di.Delete();
				}
				catch { }
			}
		}

		internal static string GetTempFolder(string folderName)
		{
			if (string.IsNullOrEmpty(folderName))
				throw new ArgumentNullException("folderName");
			return GetFolder(Path.Combine("temp", folderName.ToLowerInvariant()));
		}
	}
	#endregion
}